using System.Linq;
using System.Reflection;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Idp_Login.Configs.Id4
{
    internal static class Id4Config
    {
        //下面这行代码不要忘记了, 坑了一个上午+半个下午
        // 在任何其他可能编写cookie的中间件之前添加此项
        // app.UseCookiePolicy();//使上面的services.Configure<CookiePolicyOptions>生效

        public static void ConfigId4(this IServiceCollection services, IWebHostEnvironment environment, Id4 id4Setting)
        {
            services.ConfigureNonBreakingSameSiteCookies();//配置Cookie决策,解决Cookie跨域的策略问题

            //注入Ids4服务
            var id4Builder = services.AddIdentityServer(options =>
            {
                #region 如果是nginx + idp 集群
                if (!string.IsNullOrEmpty(id4Setting.Authority))
                {
                    //参考 https://github.com/IdentityServer/IdentityServer4/issues/324
                    options.IssuerUri = id4Setting.Authority;
                    //options.PublicOrigin = id4Setting.Authority;//id4 v4 没有了, 代码转移到startup.cs里面去了
                   
                }

                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;

                #endregion
            });//Ids4服务


            #region 添加证书
            id4Builder.AddCredential(environment, id4Setting.Certificate);
            #endregion

            #region 内存配置
            //配置身份资源
            //定义系统中的资源(用户信息)(OIDC使用的)
            id4Builder.AddInMemoryIdentityResources(Config.GetIdentityResources());

            //预置允许验证的Client
            //客户想要访问资源  把配置文件的Client配置资源放到内存 
            //还可以在配置文件里https://identityserver4.readthedocs.io/en/3.1.0/topics/clients.html
            id4Builder.AddInMemoryClients(Config.GetClients());


            #region 配置API资源
            //v4中的 InMemory 运行方式发生了 AddInMemoryApiResources 变为了 AddInMemoryApiScopes 也就是变了名字，但一定要明白这里面实际的不同。
            id4Builder.AddInMemoryApiScopes(Config.GetApiScopes());

            //id4Builder.AddInMemoryApiResources(Config.GetApiResources());

            #endregion

            //用来配合内存模式, 可以在开发和测试中用, 不建议在生产中使用
            //id4Builder.AddTestUsers(Config.GetUsers()); //给02使用时,需要取消注释, 不然会提示 unsupported_grant_type(下面有解决方法)
            //给04 05 测试时, 这行要注释掉, 不然提示  Showing login: User is not active, 

            #endregion

            #region 配置到数据库
            //https://www.cnblogs.com/stulzq/p/8120518.html
            //	<PackageReference Include="IdentityServer4.EntityFramework" Version="3.1.4" />
            /*
             * 用 ef迁移
                <ItemGroup>
		            <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
	            </ItemGroup>
             */
            /*
            var connectionString = id4Setting.ConnectionStrings.Idp;
            //从数据库读取配置,需要注释 AddInMemoryIdentityResources  AddInMemoryClients AddInMemoryApiResources
            //this adds the config data from DB (clients, resources)

            var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
            id4Builder.AddConfigurationStore(options =>
            {
            options.ConfigureDbContext = builder =>
              builder.UseSqlServer(connectionString,
                  sql => sql.MigrationsAssembly(migrationsAssembly));

            //options.ConfigureDbContext = builder =>
            //    builder.UseMySql(connectionString, new MySqlServerVersion(new Version(5, 7, 32)),
            //        sql => sql.MigrationsAssembly(migrationsAssembly)); //Pomelo.EntityFrameworkCore.MySql v5 alpha -2
            });
            // this adds the operational data from DB (codes, tokens, consents)
            id4Builder.AddOperationalStore(options =>
            {
            options.ConfigureDbContext = builder =>
              builder.UseSqlServer(connectionString,
                  sql => sql.MigrationsAssembly(migrationsAssembly));

            //options.ConfigureDbContext = builder =>
            //    builder.UseMySql(connectionString, new MySqlServerVersion(new Version(5, 7, 32)),
            //        sql => sql.MigrationsAssembly(migrationsAssembly)); //Pomelo.EntityFrameworkCore.MySql v5 alpha -2

            // this enables automatic token cleanup. this is optional.
            //options.EnableTokenCleanup = true;
            //options.TokenCleanupInterval = 30;//每隔30秒去清理下令牌
            });

            */
            #endregion

            #region identityServer4 的 redis 配置
            /*
            id4Builder.AddOperationalStore(options =>
            {
                //options.RedisConnectionString = "localhost:6379";
                //options.Db = 1;
                  options.RedisConnectionString = id4Setting.RedisCache.OperationalStoreOption.RedisConnectionString;
                options.Db = id4Setting.RedisCache.OperationalStoreOption.Db;
            });

            id4Builder.AddRedisCaching(options =>
            {
                //options.RedisConnectionString = "localhost:6379";
                //options.KeyPrefix = "prefix";

               options.RedisConnectionString = id4Setting.RedisCache.RedisCachingOption.RedisConnectionString;
                options.KeyPrefix = id4Setting.RedisCache.RedisCachingOption.KeyPrefix;
            });

            id4Builder.AddClientStoreCache<IdentityServer4.EntityFramework.Stores.ClientStore>();
            id4Builder.AddResourceStoreCache<IdentityServer4.EntityFramework.Stores.ResourceStore>();
            id4Builder.AddCorsPolicyCache<IdentityServer4.EntityFramework.Services.CorsPolicyService>();
            */
            #endregion


            //配置自定义的验证器：
            //https://www.cnblogs.com/stulzq/p/8726002.html
            //这里特别说明一下：本节讲的是“如何使用已有用户数据自定义Claim”，
            //实现 IResourceOwnerPasswordValidator 是为了对接已有的用户数据，
            //然后才是实现 IProfileService 以添加自定义 claim，
            //这两步共同完成的是 “使用已有用户数据自定义Claim”，
            //并不是自定义 Claim 就非得把两个都实现。
            id4Builder.AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>();//用户校验+[解决02密码认证ResourceOwnerPassword 在注释掉id4Builder.AddTestUsers(Config.GetUsers())后 提示 unsupported_grant_type的方法]
            id4Builder.AddProfileService<CustomProfileService>();
            //id4Builder.Services.AddTransient<IProfileService, CustomProfileService>();//这行好像不需要.

            //覆盖cookie处理程序配置
            //IdentityServer 注册两个 cookie 处理程序（一个用于身份验证会话(idsvr)，一个用于临时外部cookie(idsvr.session)）。
            //idsvr          =>  IdentityServerConstants.DefaultCookieAuthenticationScheme
            //idsvr.session  =>  IdentityServerConstants.DefaultCheckSessionCookieName

            //如果 IdentityServer 使用自定义方案在内部调用 AddAuthentication 和 AddCookie , 必须覆盖默认操作(必须在 AddIdentityServer 之后进行相同的调用。)

            //下面代码会把 idsvr给覆盖掉  变成 MyCookie1
            //services.AddAuthentication("MyCookie1")
            //    .AddCookie("MyCookie1", options =>
            //    {
            //        //options.ExpireTimeSpan = ...;
            //    });

            services.AddAuthentication();

            services.AddCors(option =>
            {
                option.AddPolicy("AllowCors", _build => _build.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
                ////某个地址跨域
                //option.AddPolicy("any", 
                //    builder => builder.WithOrigins("http://192.168.1.10:8056").AllowAnyHeader().AllowAnyMethod())
            });
        }
    }
}
